#include "ast.hpp"


namespace ast
{


Node::~Node()
{ }


void Visitor::visit(Program&) { }

void Visitor::visit(DataSection&) { }

void Visitor::visit(Directive&) { }

void Visitor::visit(TextSection&) { }

void Visitor::visit(Instruction&) { }

void Visitor::visit(Operand&) { }


Program::Program()
    : dataSections(new DataSectionList()),
      textSections(new TextSectionList())
{ }


Program::Program(DataSectionList* ds, TextSectionList* ts)
    : dataSections(ds),
      textSections(ts)
{ }


Program::~Program()
{
  free_list(dataSections);
  free_list(textSections);
}


Program* Program::clone() {
  return new Program(clone_list(dataSections), clone_list(textSections));
}


void Program::accept(Visitor* v)
{
  v->visit(*this);

  for (DataSectionList::iterator it = dataSections->begin(); it != dataSections->end(); ++it)
    (*it)->accept(v);

  for (TextSectionList::iterator it = textSections->begin(); it != textSections->end(); ++it)
    (*it)->accept(v);
}


DataSection::DataSection()
    : directives(new DirectiveList())
{ }


DataSection::DataSection(DirectiveList* dl)
    : directives(dl)
{ }


DataSection::~DataSection()
{
  free_vector(directives);
}


DataSection* DataSection::clone()
{
  return new DataSection(clone_vector(directives));
}


void DataSection::accept(Visitor* v)
{
  v->visit(*this);

  for (uint i = 0; i < directives->size(); ++i)
    directives->at(i)->accept(v);
}


Directive::Directive(int dir_type)
    : dirType(dir_type), argType(Linkage), labels(new LabelList())
{ }


Directive::Directive(int dir_type, Directive::Type arg_type)
    : dirType(dir_type), argType(arg_type), labels(new LabelList())
{ }


Directive::~Directive()
{
  free_vector(labels);
}


Directive* Directive::clone()
{
  LabelList* ll = new LabelList(labels->size());

  for (uint i = 0; i < labels->size(); ++i)
    ll->at(i) = new string(*(labels->at(i)));

  Directive* d = new Directive(dirType, argType);
  d->labels = ll;
  d->arg = arg;
  return d;
}


void Directive::accept(Visitor* v)
{
  v->visit(*this);
}


TextSection::TextSection(InstructionList* il)
    : instructions(il)
{ }


TextSection::~TextSection()
{
  free_vector(instructions);
}


TextSection* TextSection::clone()
{
  return new TextSection(clone_vector(instructions));
}


void TextSection::accept(Visitor* v)
{
  v->visit(*this);

  for (uint i = 0; i < instructions->size(); ++i)
    instructions->at(i)->accept(v);
}


Operand::Operand(Type t)
    : type(t)
{ }


Operand::Operand(Type t, int val)
    : type(t)
{
  arg.value = val;
}


Operand::Operand(Type t, Label* l)
    : type(t)
{
    arg.str = l;
}


Operand* Operand::clone()
{
  Operand* op = new Operand(type);
  op->arg = arg;
  return op;
}


Operand* Operand::getPC()
{
  return new Operand(Register, 31);
}


Operand* Operand::getR0()
{
  return new Operand(Register, 0);
}


Operand* Operand::getSP()
{
  return new Operand(Register, 30);
}


bool Operand::isConstant()
{
  switch (type) {
    case Register:
    case ZeroRegister:
      return false;

    default:
      return true;
  }
}


bool Operand::isConstantH()
{
  assert(isConstant() && "Operand is not a constant");
  return (type == ConstantH);
}


bool Operand::isConstantL()
{
  assert(isConstant() && "Operand is not a constant");
  return (type == ConstantL);
}


bool Operand::isLabel()
{
  return type == LabelC || type == LabelH;
}


bool Operand::isLabelH()
{
  return type == LabelH;
}


bool Operand::isH()
{
  return isConstantH() || isLabelH();
}


void Operand::accept(Visitor* v)
{
  v->visit(*this);
}


Instruction::Instruction(int op)
    : opcode(op), op1(0), op2(0), op3(0)
{
  init();
  operand_format = 1;
}


Instruction::Instruction(int format, Operand* a, Operand* b, Operand* c)
    : operand_format(format), op1(a), op2(b), op3(c)
{
  init();
}


Instruction::Instruction(Operand* a, Operand* b, Operand* c)
    : op1(a), op2(b), op3(c)
{
  init();
}


Instruction::Instruction(LabelList* ll,
                         int op, int format,
                         Operand* a, Operand* b, Operand* c)
    : labels(ll),
      opcode(op), operand_format(format),
      op1(a), op2(b), op3(c)
{
  init();
}


Instruction::~Instruction()
{
  delete op1;
  delete op2;
  delete op3;
}


void Instruction::init()
{
  if (op1 == NULL) op1 = Operand::getR0();
  if (op2 == NULL) op2 = Operand::getR0();
  if (op3 == NULL) op3 = Operand::getR0();
  apsBit = false;
}


Instruction* Instruction::clone()
{
  LabelList* ll = new LabelList(labels->size());

  for (uint i = 0; i < labels->size(); ++i)
    ll->at(i) = new string(*(labels->at(i)));

  return new Instruction(
      ll, opcode, operand_format,
      op1->clone(), op2->clone(), op3->clone()
  );
}


void Instruction::accept(Visitor* v)
{
  v->visit(*this);
  op1->accept(v);
  op2->accept(v);
  op3->accept(v);
}


}
